from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from datetime import datetime

db = SQLAlchemy()

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)
    is_admin = db.Column(db.Boolean, default=False)
    
    # 新增权限字段
    role = db.Column(db.String(20), default='user')  # admin, user
    can_use_dingtalk = db.Column(db.Boolean, default=True)  # 钉钉消息任务模块权限
    can_use_custom_tasks = db.Column(db.Boolean, default=True)  # 用户自定义模块权限
    can_manage_packages = db.Column(db.Boolean, default=False)  # 依赖项管理权限
    can_manage_users = db.Column(db.Boolean, default=False)  # 用户管理权限
    
    def has_permission(self, permission):
        """检查用户是否有指定权限"""
        # 管理员拥有所有权限
        if self.is_admin or self.username == 'admin' or self.role == 'admin':
            return True
        
        permission_map = {
            'dingtalk': self.can_use_dingtalk,
            'custom_tasks': self.can_use_custom_tasks,
            'packages': self.can_manage_packages,
            'users': self.can_manage_users
        }
        
        return permission_map.get(permission, False)
    
    def can_view_user_data(self, target_user_id):
        """检查是否可以查看指定用户的数据"""
        # 管理员可以查看所有用户的数据
        if self.is_admin or self.username == 'admin' or self.role == 'admin':
            return True
        return self.id == target_user_id
    
    def is_administrator(self):
        """检查用户是否为管理员"""
        return self.is_admin or self.username == 'admin' or self.role == 'admin'
    
    def get_dependencies(self):
        """获取用户相关的所有依赖项"""
        dependencies = {
            'dingtalk_robots': [],
            'dingtalk_tasks': [],
            'custom_scripts': [],
            'custom_tasks': [],
            'background_tasks': [],
            'package_logs': []
        }
        
        # 检查钉钉机器人
        dependencies['dingtalk_robots'] = DingTalkRobot.query.filter_by(user_id=self.id).all()
        
        # 检查钉钉任务
        dependencies['dingtalk_tasks'] = ScheduledTask.query.filter_by(user_id=self.id).all()
        
        # 检查自定义脚本
        dependencies['custom_scripts'] = CustomScript.query.filter_by(user_id=self.id).all()
        
        # 检查自定义任务
        dependencies['custom_tasks'] = CustomTask.query.filter_by(user_id=self.id).all()
        
        # 检查后台任务
        dependencies['background_tasks'] = BackgroundInstallationTask.query.filter_by(user_id=self.id).all()
        
        # 检查包安装日志
        dependencies['package_logs'] = PackageInstallationLog.query.filter_by(user_id=self.id).all()
        
        return dependencies
    
    def has_dependencies(self):
        """检查用户是否有相关依赖项"""
        dependencies = self.get_dependencies()
        return any(dependencies.values())
    
    def get_dependencies_summary(self):
        """获取依赖项摘要信息"""
        dependencies = self.get_dependencies()
        summary = {}
        
        for key, items in dependencies.items():
            if items:
                summary[key] = {
                    'count': len(items),
                    'items': [{'id': item.id, 'name': getattr(item, 'name', str(item.id))} for item in items]
                }
        
        return summary
    
    def can_be_deleted(self):
        """检查用户是否可以安全删除"""
        return not self.has_dependencies() and not self.is_administrator()
    
    def transfer_dependencies(self, new_user_id):
        """将用户的所有依赖项转移给新用户"""
        import logging
        logger = logging.getLogger(__name__)
        
        # 记录转移开始
        logger.info(f"=== 开始转移依赖项 ===")
        logger.info(f"源用户: {self.username} (ID: {self.id})")
        logger.info(f"目标用户ID: {new_user_id}")
        
        if not new_user_id:
            logger.error("转移失败: 新用户ID不能为空")
            return False, "新用户ID不能为空"
        
        new_user = User.query.get(new_user_id)
        if not new_user:
            logger.error(f"转移失败: 指定的继承人用户不存在 (ID: {new_user_id})")
            return False, "指定的继承人用户不存在"
        
        logger.info(f"目标用户: {new_user.username} (ID: {new_user.id})")
        
        if new_user.id == self.id:
            logger.error("转移失败: 不能将依赖项转移给自己")
            return False, "不能将依赖项转移给自己"
        
        # 记录转移前的数据状态
        logger.info(f"=== 转移前数据状态 ===")
        self._log_user_data_status("源用户", self.id)
        self._log_user_data_status("目标用户", new_user.id)
        
        # 记录目标用户转移前的数据数量（用于验证）
        target_robots_before = DingTalkRobot.query.filter_by(user_id=new_user_id).count()
        target_tasks_before = ScheduledTask.query.filter_by(user_id=new_user_id).count()
        target_scripts_before = CustomScript.query.filter_by(user_id=new_user_id).count()
        target_custom_tasks_before = CustomTask.query.filter_by(user_id=new_user_id).count()
        target_background_tasks_before = BackgroundInstallationTask.query.filter_by(user_id=new_user_id).count()
        target_package_logs_before = PackageInstallationLog.query.filter_by(user_id=new_user_id).count()
        
        logger.info(f"目标用户转移前数据:")
        logger.info(f"  - 钉钉机器人: {target_robots_before}个")
        logger.info(f"  - 钉钉任务: {target_tasks_before}个")
        logger.info(f"  - 自定义脚本: {target_scripts_before}个")
        logger.info(f"  - 自定义任务: {target_custom_tasks_before}个")
        logger.info(f"  - 后台任务: {target_background_tasks_before}个")
        logger.info(f"  - 包安装日志: {target_package_logs_before}条")
        
        try:
            # 记录要转移的数据
            logger.info(f"=== 开始转移数据 ===")
            
            # 转移钉钉机器人
            robots_before = DingTalkRobot.query.filter_by(user_id=self.id).all()
            robots_count = len(robots_before)
            logger.info(f"转移钉钉机器人: {robots_count}个")
            for robot in robots_before:
                logger.info(f"  - 机器人: {robot.name} (ID: {robot.id})")
            
            DingTalkRobot.query.filter_by(user_id=self.id).update({'user_id': new_user_id})
            
            # 转移钉钉任务
            tasks_before = ScheduledTask.query.filter_by(user_id=self.id).all()
            tasks_count = len(tasks_before)
            logger.info(f"转移钉钉任务: {tasks_count}个")
            for task in tasks_before:
                logger.info(f"  - 任务: {task.name} (ID: {task.id})")
            
            ScheduledTask.query.filter_by(user_id=self.id).update({'user_id': new_user_id})
            
            # 转移自定义脚本
            scripts_before = CustomScript.query.filter_by(user_id=self.id).all()
            scripts_count = len(scripts_before)
            logger.info(f"转移自定义脚本: {scripts_count}个")
            for script in scripts_before:
                logger.info(f"  - 脚本: {script.name} (ID: {script.id})")
            
            CustomScript.query.filter_by(user_id=self.id).update({'user_id': new_user_id})
            
            # 转移自定义任务
            custom_tasks_before = CustomTask.query.filter_by(user_id=self.id).all()
            custom_tasks_count = len(custom_tasks_before)
            logger.info(f"转移自定义任务: {custom_tasks_count}个")
            for task in custom_tasks_before:
                logger.info(f"  - 自定义任务: {task.name} (ID: {task.id})")
            
            CustomTask.query.filter_by(user_id=self.id).update({'user_id': new_user_id})
            
            # 转移后台任务
            background_tasks_before = BackgroundInstallationTask.query.filter_by(user_id=self.id).all()
            background_tasks_count = len(background_tasks_before)
            logger.info(f"转移后台任务: {background_tasks_count}个")
            for task in background_tasks_before:
                logger.info(f"  - 后台任务: {task.package_name} (ID: {task.id})")
            
            BackgroundInstallationTask.query.filter_by(user_id=self.id).update({'user_id': new_user_id})
            
            # 转移包安装日志
            package_logs_before = PackageInstallationLog.query.filter_by(user_id=self.id).all()
            package_logs_count = len(package_logs_before)
            logger.info(f"转移包安装日志: {package_logs_count}条")
            for log in package_logs_before:
                logger.info(f"  - 包日志: {log.package_name} (ID: {log.id})")
            
            PackageInstallationLog.query.filter_by(user_id=self.id).update({'user_id': new_user_id})
            
            # 提交数据库事务
            db.session.commit()
            logger.info(f"数据库事务提交成功")
            
            # 转移文件系统中的文件夹和脚本（在数据库事务提交后）
            logger.info(f"=== 开始转移文件系统 ===")
            success = self._transfer_file_system(new_user_id, new_user.username)
            if not success:
                logger.error(f"文件系统转移失败")
                # 注意：这里不能回滚数据库事务，因为文件系统操作在数据库提交之后
                # 如果文件系统转移失败，数据库中的记录已经转移，但文件还在原位置
                # 这种情况下需要手动处理或通知管理员
                return False, "文件系统转移失败，但数据库记录已转移"
            
            # 记录转移后的数据状态
            logger.info(f"=== 转移后数据状态 ===")
            self._log_user_data_status("源用户", self.id)
            self._log_user_data_status("目标用户", new_user.id)
            
            # 验证转移结果
            logger.info(f"=== 验证转移结果 ===")
            
            # 计算期望数量
            # 期望数量 = 转移的数量 + 目标用户原本的数量
            expected_counts = {
                'robots': robots_count + target_robots_before,
                'tasks': tasks_count + target_tasks_before,
                'scripts': scripts_count + target_scripts_before,
                'custom_tasks': custom_tasks_count + target_custom_tasks_before,
                'background_tasks': background_tasks_count + target_background_tasks_before,
                'package_logs': package_logs_count + target_package_logs_before
            }
            
            success = self._verify_transfer(new_user_id, expected_counts)
            
            if success:
                logger.info(f"✅ 转移成功完成")
                return True, f"成功将 {self.username} 的所有依赖项转移给 {new_user.username}"
            else:
                logger.error(f"❌ 转移验证失败")
                return False, "转移验证失败"
            
        except Exception as e:
            db.session.rollback()
            logger.error(f"转移失败: {str(e)}")
            logger.error(f"数据库事务已回滚")
            return False, f"转移依赖项时发生错误: {str(e)}"
    
    def _log_user_data_status(self, user_label, user_id):
        """记录用户数据状态"""
        import logging
        logger = logging.getLogger(__name__)
        
        robots = DingTalkRobot.query.filter_by(user_id=user_id).all()
        tasks = ScheduledTask.query.filter_by(user_id=user_id).all()
        scripts = CustomScript.query.filter_by(user_id=user_id).all()
        custom_tasks = CustomTask.query.filter_by(user_id=user_id).all()
        background_tasks = BackgroundInstallationTask.query.filter_by(user_id=user_id).all()
        package_logs = PackageInstallationLog.query.filter_by(user_id=user_id).all()
        
        logger.info(f"{user_label} (ID: {user_id}) 数据状态:")
        logger.info(f"  - 钉钉机器人: {len(robots)}个")
        logger.info(f"  - 钉钉任务: {len(tasks)}个")
        logger.info(f"  - 自定义脚本: {len(scripts)}个")
        logger.info(f"  - 自定义任务: {len(custom_tasks)}个")
        logger.info(f"  - 后台任务: {len(background_tasks)}个")
        logger.info(f"  - 包安装日志: {len(package_logs)}条")
    
    def _verify_transfer(self, new_user_id, expected_counts):
        """验证转移结果"""
        import logging
        logger = logging.getLogger(__name__)
        
        # 检查源用户数据是否清空
        source_robots = DingTalkRobot.query.filter_by(user_id=self.id).count()
        source_tasks = ScheduledTask.query.filter_by(user_id=self.id).count()
        source_scripts = CustomScript.query.filter_by(user_id=self.id).count()
        source_custom_tasks = CustomTask.query.filter_by(user_id=self.id).count()
        source_background_tasks = BackgroundInstallationTask.query.filter_by(user_id=self.id).count()
        source_package_logs = PackageInstallationLog.query.filter_by(user_id=self.id).count()
        
        logger.info(f"源用户数据验证:")
        logger.info(f"  - 钉钉机器人: {source_robots}个 (期望: 0个)")
        logger.info(f"  - 钉钉任务: {source_tasks}个 (期望: 0个)")
        logger.info(f"  - 自定义脚本: {source_scripts}个 (期望: 0个)")
        logger.info(f"  - 自定义任务: {source_custom_tasks}个 (期望: 0个)")
        logger.info(f"  - 后台任务: {source_background_tasks}个 (期望: 0个)")
        logger.info(f"  - 包安装日志: {source_package_logs}条 (期望: 0条)")
        
        # 检查目标用户数据是否正确
        target_robots = DingTalkRobot.query.filter_by(user_id=new_user_id).count()
        target_tasks = ScheduledTask.query.filter_by(user_id=new_user_id).count()
        target_scripts = CustomScript.query.filter_by(user_id=new_user_id).count()
        target_custom_tasks = CustomTask.query.filter_by(user_id=new_user_id).count()
        target_background_tasks = BackgroundInstallationTask.query.filter_by(user_id=new_user_id).count()
        target_package_logs = PackageInstallationLog.query.filter_by(user_id=new_user_id).count()
        
        logger.info(f"目标用户数据验证:")
        logger.info(f"  - 钉钉机器人: {target_robots}个 (期望: {expected_counts['robots']}个)")
        logger.info(f"  - 钉钉任务: {target_tasks}个 (期望: {expected_counts['tasks']}个)")
        logger.info(f"  - 自定义脚本: {target_scripts}个 (期望: {expected_counts['scripts']}个)")
        logger.info(f"  - 自定义任务: {target_custom_tasks}个 (期望: {expected_counts['custom_tasks']}个)")
        logger.info(f"  - 后台任务: {target_background_tasks}个 (期望: {expected_counts['background_tasks']}个)")
        logger.info(f"  - 包安装日志: {target_package_logs}条 (期望: {expected_counts['package_logs']}条)")
        
        # 验证结果
        source_ok = (source_robots == 0 and source_tasks == 0 and source_scripts == 0 and 
                    source_custom_tasks == 0 and source_background_tasks == 0 and source_package_logs == 0)
        
        target_ok = (target_robots == expected_counts['robots'] and 
                    target_tasks == expected_counts['tasks'] and 
                    target_scripts == expected_counts['scripts'] and 
                    target_custom_tasks == expected_counts['custom_tasks'] and 
                    target_background_tasks == expected_counts['background_tasks'] and 
                    target_package_logs == expected_counts['package_logs'])
        
        if source_ok and target_ok:
            # 验证文件系统转移
            logger.info(f"=== 验证文件系统转移 ===")
            file_system_ok = self._verify_file_system_transfer(new_user_id)
            
            if file_system_ok:
                logger.info(f"✅ 转移验证通过")
                return True
            else:
                logger.error(f"❌ 文件系统转移验证失败")
                return False
        else:
            logger.error(f"❌ 转移验证失败")
            if not source_ok:
                logger.error(f"  源用户数据未完全清空")
            if not target_ok:
                logger.error(f"  目标用户数据不匹配")
            return False
    
    def _verify_file_system_transfer(self, new_user_id):
        """验证文件系统转移结果"""
        import os
        import logging
        logger = logging.getLogger(__name__)
        
        try:
            source_folder = f"custom_scripts/{self.id}"
            target_folder = f"custom_scripts/{new_user_id}"
            
            # 检查源文件夹是否已删除
            source_exists = os.path.exists(source_folder)
            target_exists = os.path.exists(target_folder)
            
            logger.info(f"源文件夹存在: {source_exists} (期望: False)")
            logger.info(f"目标文件夹存在: {target_exists} (期望: True)")
            
            if source_exists:
                logger.error(f"源文件夹未删除: {source_folder}")
                return False
            
            if not target_exists:
                logger.error(f"目标文件夹不存在: {target_folder}")
                return False
            
            # 检查目标文件夹中的文件数量
            if target_exists:
                target_files = []
                for root, dirs, files in os.walk(target_folder):
                    for file in files:
                        target_files.append(os.path.join(root, file))
                
                logger.info(f"目标文件夹文件数量: {len(target_files)}")
                
                # 验证数据库中的脚本路径是否正确
                custom_scripts = CustomScript.query.filter_by(user_id=new_user_id).all()
                for script in custom_scripts:
                    if not os.path.exists(script.file_path):
                        logger.error(f"脚本文件不存在: {script.file_path}")
                        return False
                    logger.info(f"脚本文件验证通过: {script.name} - {script.file_path}")
            
            logger.info(f"✅ 文件系统转移验证通过")
            return True
            
        except Exception as e:
            logger.error(f"文件系统验证失败: {str(e)}")
            return False
    
    def _transfer_file_system(self, new_user_id, new_username):
        """转移文件系统中的文件夹和脚本，包含重名规避策略"""
        import os
        import shutil
        import logging
        from datetime import datetime
        logger = logging.getLogger(__name__)
        
        try:
            # 源用户和目标用户的文件夹路径
            source_folder = f"custom_scripts/{self.id}"
            target_folder = f"custom_scripts/{new_user_id}"
            
            logger.info(f"源文件夹: {source_folder}")
            logger.info(f"目标文件夹: {target_folder}")
            
            # 检查源文件夹是否存在
            if not os.path.exists(source_folder):
                logger.info(f"源文件夹不存在，创建目标文件夹")
                # 即使源文件夹不存在，也要创建目标文件夹
                os.makedirs(target_folder, exist_ok=True)
                logger.info(f"目标文件夹已创建")
                return True
            
            # 创建目标文件夹
            os.makedirs(target_folder, exist_ok=True)
            logger.info(f"目标文件夹已创建")
            
            # 更新数据库中脚本的文件路径（在转移文件之前）
            logger.info(f"更新数据库中脚本的文件路径")
            custom_scripts = CustomScript.query.filter_by(user_id=new_user_id).all()
            path_mapping = {}  # 记录路径映射关系
            
            for script in custom_scripts:
                # 更新文件路径，将用户ID替换为新用户ID
                old_path = script.file_path
                new_path = old_path.replace(f"/{self.id}/", f"/{new_user_id}/")
                script.file_path = new_path
                path_mapping[old_path] = new_path
                logger.info(f"更新脚本路径: {script.name} - {old_path} -> {new_path}")
            
            # 提交路径更新
            db.session.commit()
            logger.info(f"脚本路径更新已提交")
            
            # 转移所有文件和文件夹，包含重名规避策略
            transferred_items = []
            source_items = os.listdir(source_folder)
            
            if not source_items:
                logger.info(f"源文件夹为空，直接删除")
            else:
                for item in source_items:
                    source_path = os.path.join(source_folder, item)
                    original_target_path = os.path.join(target_folder, item)
                    
                    # 处理重名冲突
                    target_path = self._resolve_name_conflict(original_target_path, logger)
                    
                    try:
                        if os.path.isfile(source_path):
                            # 转移文件
                            shutil.copy2(source_path, target_path)
                            transferred_name = os.path.basename(target_path)
                            logger.info(f"转移文件: {item} -> {transferred_name}")
                            transferred_items.append(f"文件: {item} -> {transferred_name}")
                            
                            # 更新数据库中对应的脚本路径
                            self._update_script_paths_for_file(item, transferred_name, path_mapping, logger)
                            
                        elif os.path.isdir(source_path):
                            # 转移文件夹
                            shutil.copytree(source_path, target_path, dirs_exist_ok=True)
                            transferred_name = os.path.basename(target_path)
                            logger.info(f"转移文件夹: {item} -> {transferred_name}")
                            transferred_items.append(f"文件夹: {item} -> {transferred_name}")
                            
                            # 更新数据库中对应的脚本路径
                            self._update_script_paths_for_folder(item, transferred_name, path_mapping, logger)
                            
                    except Exception as e:
                        logger.error(f"转移 {item} 失败: {str(e)}")
                        return False
            
            # 删除源文件夹（无论是否为空）
            shutil.rmtree(source_folder)
            logger.info(f"源文件夹已删除: {source_folder}")
            
            logger.info(f"文件系统转移完成，共转移 {len(transferred_items)} 个项目")
            for item in transferred_items:
                logger.info(f"  - {item}")
            
            return True
            
        except Exception as e:
            logger.error(f"文件系统转移失败: {str(e)}")
            return False
    
    def _resolve_name_conflict(self, target_path, logger):
        """解决重名冲突，返回新的路径"""
        import os
        
        if not os.path.exists(target_path):
            return target_path
        
        # 获取路径信息
        directory = os.path.dirname(target_path)
        filename = os.path.basename(target_path)
        name, ext = os.path.splitext(filename)
        
        # 生成时间戳后缀
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # 尝试不同的后缀
        counter = 1
        while True:
            if ext:
                new_name = f"{name}_{timestamp}_{counter}{ext}"
            else:
                new_name = f"{name}_{timestamp}_{counter}"
            
            new_path = os.path.join(directory, new_name)
            
            if not os.path.exists(new_path):
                logger.info(f"重名冲突解决: {filename} -> {new_name}")
                return new_path
            
            counter += 1
    
    def _update_script_paths_for_file(self, original_name, new_name, path_mapping, logger):
        """更新数据库中单个文件对应的脚本路径"""
        import os
        
        # 查找需要更新的脚本
        custom_scripts = CustomScript.query.filter_by(user_id=self.id).all()
        for script in custom_scripts:
            if os.path.basename(script.file_path) == original_name:
                # 更新文件路径
                old_path = script.file_path
                new_path = old_path.replace(original_name, new_name)
                script.file_path = new_path
                path_mapping[old_path] = new_path
                logger.info(f"更新脚本文件路径: {script.name} - {old_path} -> {new_path}")
        
        # 提交更改
        db.session.commit()
    
    def _update_script_paths_for_folder(self, original_folder, new_folder, path_mapping, logger):
        """更新数据库中文件夹内所有脚本的路径"""
        import os
        
        # 查找需要更新的脚本
        custom_scripts = CustomScript.query.filter_by(user_id=self.id).all()
        updated_count = 0
        
        for script in custom_scripts:
            if original_folder in script.file_path:
                # 更新文件路径
                old_path = script.file_path
                new_path = old_path.replace(original_folder, new_folder)
                script.file_path = new_path
                path_mapping[old_path] = new_path
                logger.info(f"更新脚本文件夹路径: {script.name} - {old_path} -> {new_path}")
                updated_count += 1
        
        if updated_count > 0:
            # 提交更改
            db.session.commit()
            logger.info(f"更新了 {updated_count} 个脚本的文件夹路径")

class DingTalkRobot(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    webhook = db.Column(db.String(200), nullable=False)
    secret = db.Column(db.String(100))
    tag = db.Column(db.String(100), nullable=False)  # 机器人标签，用于区分群组
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref='robots')

# 任务机器人关联表
class TaskRobotAssociation(db.Model):
    """任务与机器人的多对多关联表"""
    __tablename__ = 'task_robot_association'
    
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.Integer, db.ForeignKey('scheduled_task.id'), nullable=False)
    robot_id = db.Column(db.Integer, db.ForeignKey('ding_talk_robot.id'), nullable=False)
    at_persons = db.Column(db.Text, nullable=True)  # @人的手机号列表，多个手机号用逗号分隔
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    
    # 关联关系
    task = db.relationship('ScheduledTask', backref='robot_associations')
    robot = db.relationship('DingTalkRobot', backref='task_associations')

class ScheduledTask(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    cron_expression = db.Column(db.String(100), nullable=False)
    script_path = db.Column(db.String(200), nullable=False)
    status = db.Column(db.String(20), default='pending')
    is_active = db.Column(db.Boolean, default=True)
    # 保留原有的robot_id字段以保持向后兼容
    robot_id = db.Column(db.Integer, db.ForeignKey('ding_talk_robot.id'))
    robot = db.relationship('DingTalkRobot', backref='tasks')
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref='tasks')
    last_run_time = db.Column(db.DateTime)
    next_run_time = db.Column(db.DateTime)
    last_run_duration = db.Column(db.Float)
    params = db.Column(db.Text, nullable=True)
    last_run_status = db.Column(db.String(20), nullable=True)
    # 重试相关字段
    max_retries = db.Column(db.Integer, default=3)  # 最大重试次数
    retry_delay = db.Column(db.Integer, default=300)  # 重试延迟（秒）
    retry_count = db.Column(db.Integer, default=0)  # 当前重试次数
    last_error = db.Column(db.Text, nullable=True)  # 最后一次错误信息
    
    @property
    def robots(self):
        """获取任务关联的所有机器人"""
        return [assoc.robot for assoc in self.robot_associations]
    
    @property
    def robot_ids(self):
        """获取任务关联的所有机器人ID列表"""
        return [assoc.robot_id for assoc in self.robot_associations]
    
    def add_robot(self, robot):
        """添加机器人到任务"""
        if robot not in self.robots:
            association = TaskRobotAssociation(task_id=self.id, robot_id=robot.id)
            db.session.add(association)
            db.session.commit()
    
    def remove_robot(self, robot):
        """从任务中移除机器人"""
        association = TaskRobotAssociation.query.filter_by(
            task_id=self.id, robot_id=robot.id
        ).first()
        if association:
            db.session.delete(association)
            db.session.commit()
    
    def set_robots(self, robot_ids, robot_at_configs=None):
        """设置任务关联的机器人列表和@人配置"""
        # 清除现有关联
        TaskRobotAssociation.query.filter_by(task_id=self.id).delete()
        
        # 添加新的关联
        for robot_id in robot_ids:
            if robot_id:  # 确保robot_id不为空
                # 获取该机器人的@人配置
                at_persons = ''
                if robot_at_configs and str(robot_id) in robot_at_configs:
                    at_persons = robot_at_configs[str(robot_id)]
                
                association = TaskRobotAssociation(
                    task_id=self.id, 
                    robot_id=robot_id,
                    at_persons=at_persons
                )
                db.session.add(association)
        
        db.session.commit()
    
    def get_robot_at_configs(self):
        """获取所有机器人的@人配置"""
        configs = {}
        for assoc in self.robot_associations:
            if assoc.at_persons:
                configs[str(assoc.robot_id)] = assoc.at_persons
        return configs

class ScriptVersion(db.Model):
    """脚本版本模型"""
    id = db.Column(db.Integer, primary_key=True)
    script_id = db.Column(db.Integer, db.ForeignKey('script.id'), nullable=False)
    version = db.Column(db.Integer, nullable=False)
    content = db.Column(db.Text, nullable=False)
    commit_message = db.Column(db.Text)
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    created_by_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    created_by = db.relationship('User', backref='script_versions')
    script = db.relationship('Script', backref=db.backref('versions', order_by='desc(ScriptVersion.version)'))

class Script(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    path = db.Column(db.String(200), nullable=False)
    current_version = db.Column(db.Integer, default=1)
    upload_time = db.Column(db.DateTime, server_default=db.func.now())
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref='scripts')

class TaskExecutionLog(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.Integer, db.ForeignKey('scheduled_task.id'))
    task = db.relationship('ScheduledTask', backref='execution_logs')
    start_time = db.Column(db.DateTime, nullable=False)
    end_time = db.Column(db.DateTime, nullable=False)
    duration = db.Column(db.Float, nullable=False)
    status = db.Column(db.String(20), nullable=False)
    log_content = db.Column(db.Text, nullable=True)
    log_type = db.Column(db.String(20), default='script_execution', info={'choices': ['system_log', 'dingtalk_log', 'script_execution']})
    created_at = db.Column(db.DateTime, server_default=db.func.now())

# 用户自定义定时任务相关模型
class CustomScript(db.Model):
    """用户自定义脚本模型"""
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text, nullable=True)
    filename = db.Column(db.String(200), nullable=False)
    file_path = db.Column(db.String(500), nullable=False)
    file_size = db.Column(db.Integer, nullable=False)
    upload_time = db.Column(db.DateTime, server_default=db.func.now())
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref='custom_scripts')
    is_active = db.Column(db.Boolean, default=True)

class CustomTask(db.Model):
    """用户自定义定时任务模型"""
    __table_args__ = (
        db.UniqueConstraint('name', 'user_id', name='unique_task_name_per_user'),
    )
    
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text, nullable=True)
    cron_expression = db.Column(db.String(100), nullable=False)
    script_id = db.Column(db.Integer, db.ForeignKey('custom_script.id'))
    script = db.relationship('CustomScript', backref='tasks')
    parameters = db.Column(db.Text, nullable=True)  # JSON格式存储参数
    is_active = db.Column(db.Boolean, default=True)
    status = db.Column(db.String(20), default='pending')  # 添加status字段
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref='custom_tasks')
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    last_run_time = db.Column(db.DateTime, nullable=True)
    next_run_time = db.Column(db.DateTime, nullable=True)
    last_run_status = db.Column(db.String(20), nullable=True)
    last_run_duration = db.Column(db.Float, nullable=True)
    # 重试相关字段
    max_retries = db.Column(db.Integer, default=3)  # 最大重试次数
    retry_delay = db.Column(db.Integer, default=300)  # 重试延迟（秒）
    retry_count = db.Column(db.Integer, default=0)  # 当前重试次数
    last_error = db.Column(db.Text, nullable=True)  # 最后一次错误信息

class CustomTaskLog(db.Model):
    """用户自定义定时任务执行日志模型"""
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.Integer, db.ForeignKey('custom_task.id'))
    task = db.relationship('CustomTask', backref='execution_logs')
    start_time = db.Column(db.DateTime, nullable=False)
    end_time = db.Column(db.DateTime, nullable=True)
    duration = db.Column(db.Float, nullable=True)
    status = db.Column(db.String(20), nullable=False)  # 运行中、成功、失败
    output = db.Column(db.Text, nullable=True)  # 脚本输出
    error_message = db.Column(db.Text, nullable=True)  # 错误信息
    created_at = db.Column(db.DateTime, server_default=db.func.now())

# 依赖项管理相关模型
class PackageInstallationLog(db.Model):
    """包安装日志模型"""
    id = db.Column(db.Integer, primary_key=True)
    package_name = db.Column(db.String(200), nullable=False)
    version = db.Column(db.String(100), nullable=True)
    operation = db.Column(db.String(50), nullable=False)  # install, uninstall, upgrade
    status = db.Column(db.String(20), nullable=False)  # success, failed, running
    output = db.Column(db.Text, nullable=True)  # 操作输出
    error_message = db.Column(db.Text, nullable=True)  # 错误信息
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref='package_logs')
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    duration = db.Column(db.Float, nullable=True)  # 操作耗时（秒）

class InstalledPackage(db.Model):
    """已安装包信息模型"""
    id = db.Column(db.Integer, primary_key=True)
    package_name = db.Column(db.String(200), nullable=False, unique=True)
    version = db.Column(db.String(100), nullable=True)
    summary = db.Column(db.Text, nullable=True)
    location = db.Column(db.String(500), nullable=True)
    requires = db.Column(db.Text, nullable=True)  # JSON格式存储依赖关系
    installed_at = db.Column(db.DateTime, server_default=db.func.now())
    last_updated = db.Column(db.DateTime, server_default=db.func.now(), onupdate=db.func.now())

# 在现有的模型之后添加后台安装任务模型

class BackgroundInstallationTask(db.Model):
    """后台安装任务模型"""
    __tablename__ = 'background_installation_tasks'
    
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    package_name = db.Column(db.String(100), nullable=False)
    version = db.Column(db.String(50))
    operation = db.Column(db.String(20), nullable=False)  # install, upgrade, uninstall
    status = db.Column(db.String(20), default='running')  # running, success, failed, cancelled
    mirror_source = db.Column(db.String(200))
    progress_messages = db.Column(db.Text)  # JSON格式存储进度消息
    start_time = db.Column(db.DateTime, default=datetime.utcnow)
    end_time = db.Column(db.DateTime)
    duration = db.Column(db.Float)  # 运行时间（秒）
    output = db.Column(db.Text)
    error_message = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    # 关联关系
    user = db.relationship('User', backref='background_tasks')
    
    def to_dict(self):
        """转换为字典格式"""
        return {
            'id': self.id,
            'user_id': self.user_id,
            'package_name': self.package_name,
            'version': self.version,
            'operation': self.operation,
            'status': self.status,
            'mirror_source': self.mirror_source,
            'progress_messages': self.progress_messages,
            'start_time': self.start_time.isoformat() if self.start_time else None,
            'end_time': self.end_time.isoformat() if self.end_time else None,
            'duration': self.duration,
            'output': self.output,
            'error_message': self.error_message,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'updated_at': self.updated_at.isoformat() if self.updated_at else None
        }
    
    def get_elapsed_time(self):
        """获取已运行时间"""
        if self.start_time:
            if self.end_time:
                return (self.end_time - self.start_time).total_seconds()
            else:
                return (datetime.utcnow() - self.start_time).total_seconds()
        return 0
    
    def is_running(self):
        """检查是否正在运行"""
        return self.status == 'running'
    
    def is_completed(self):
        """检查是否已完成"""
        return self.status in ['success', 'failed', 'cancelled']